// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
#define V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_

#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/bytecodes.h"
#include "src/zone/zone-containers.h"

namespace v8 {
namespace internal {
    namespace interpreter {

        // A class that allows the allocation of contiguous temporary registers.
        class BytecodeRegisterAllocator final {
        public:
            // Enables observation of register allocation and free events.
            class Observer {
            public:
                virtual ~Observer() = default;
                virtual void RegisterAllocateEvent(Register reg) = 0;
                virtual void RegisterListAllocateEvent(RegisterList reg_list) = 0;
                virtual void RegisterListFreeEvent(RegisterList reg_list) = 0;
            };

            explicit BytecodeRegisterAllocator(int start_index)
                : next_register_index_(start_index)
                , max_register_count_(start_index)
                , observer_(nullptr)
            {
            }
            ~BytecodeRegisterAllocator() = default;

            // Returns a new register.
            Register NewRegister()
            {
                Register reg(next_register_index_++);
                max_register_count_ = std::max(next_register_index_, max_register_count_);
                if (observer_) {
                    observer_->RegisterAllocateEvent(reg);
                }
                return reg;
            }

            // Returns a consecutive list of |count| new registers.
            RegisterList NewRegisterList(int count)
            {
                RegisterList reg_list(next_register_index_, count);
                next_register_index_ += count;
                max_register_count_ = std::max(next_register_index_, max_register_count_);
                if (observer_) {
                    observer_->RegisterListAllocateEvent(reg_list);
                }
                return reg_list;
            }

            // Returns a growable register list.
            RegisterList NewGrowableRegisterList()
            {
                RegisterList reg_list(next_register_index_, 0);
                return reg_list;
            }

            // Appends a new register to |reg_list| increasing it's count by one and
            // returning the register added.
            //
            // Note: no other new registers must be currently allocated since the register
            // list was originally allocated.
            Register GrowRegisterList(RegisterList* reg_list)
            {
                Register reg(NewRegister());
                reg_list->IncrementRegisterCount();
                // If the following CHECK fails then a register was allocated (and not
                // freed) between the creation of the RegisterList and this call to add a
                // Register.
                CHECK_EQ(reg.index(), reg_list->last_register().index());
                return reg;
            }

            // Release all registers above |register_index|.
            void ReleaseRegisters(int register_index)
            {
                int count = next_register_index_ - register_index;
                next_register_index_ = register_index;
                if (observer_) {
                    observer_->RegisterListFreeEvent(RegisterList(register_index, count));
                }
            }

            // Returns true if the register |reg| is a live register.
            bool RegisterIsLive(Register reg) const
            {
                return reg.index() < next_register_index_;
            }

            // Returns a register list for all currently live registers.
            RegisterList AllLiveRegisters() const
            {
                return RegisterList(0, next_register_index());
            }

            void set_observer(Observer* observer) { observer_ = observer; }

            int next_register_index() const { return next_register_index_; }
            int maximum_register_count() const { return max_register_count_; }

        private:
            int next_register_index_;
            int max_register_count_;
            Observer* observer_;

            DISALLOW_COPY_AND_ASSIGN(BytecodeRegisterAllocator);
        };

    } // namespace interpreter
} // namespace internal
} // namespace v8

#endif // V8_INTERPRETER_BYTECODE_REGISTER_ALLOCATOR_H_
